home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Toolbox / Visual Basic Toolbox (P.I.E.)(1996).ISO / tpascal / bpvbx / tpm3.txt < prev    next >
Encoding:
Text File  |  1995-03-24  |  11.0 KB  |  242 lines

  1. Creating Visual Basic VBXs:
  2. The Event List
  3.   by Fred C. Hill
  4.  
  5. The third in a series of articles on the secrets of writing VBX controls in Borland Pascal.
  6.  
  7. In this installment we'll explore then process by which the Visual Basic custom control allows the user to become an integral part of the operation of the control.  The property list (See The Pascal Magazine, issue 2) provides a communication path but would be very unwieldy if it were the only way to manipulate a control.  Imagine what your program would look like if you had to constantly monitor changes in the properties in order to know what your control was doing.
  8. Declaring the Event
  9.    The Visual Basic Event List provides the additional level to allow the developer to interact on a real time basis.  In addition to the Windows GUI, Windows 3.1 provides a true event processor.  The current version of Windows still only provides cooperative multi-tasking and this is accomplished through the event processor.
  10.    When declaring an Event in the event list you're really only providing Visual Basic with the event name and its arguments. The actual processing behind the Event takes place in the control procedure.  
  11.    In our project for this issue we will be converting the CIRC2 code provided in the Custom Control Development Kit (CDK) from Microsoft.  This project is used to define how to add a custom event however we will be using it to explain both the standard and the custom events.
  12.     Event lists are declared in the same way as the Property lists. In our example we will be using an enumerated list and let the compiler determine the value assigned to the particular entry.
  13.  
  14. The Event Unit
  15. unit event;
  16. interface
  17. uses
  18.     WinTypes,
  19.     vbapi_;
  20.  
  21. { Event list
  22.  Define the consecutive indices for the events
  23. }
  24. type
  25.     EVENT_Index = (
  26.         EVENT_Circle_ClickIn,
  27.         EVENT_Circle_ClickOut,
  28.         EVENT_Circle_DRAGDROP,
  29.         EVENT_Circle_DRAGOVER,
  30.         EVENT_Circle_Last);
  31.  
  32. {Event procedure parameter prototypes}
  33. const
  34.     ParamTypes_ClickIn:    array[0..1]of word = (ET_R4, ET_R4);
  35.  
  36.     EventClickInName:    array[0..8] of Char = 'ClickIn'#0;
  37.     EventClickInParm: 
  38.         array[0..24] of char = 'X As Single, Y As Single'#0;
  39.  
  40.     Event_ClickIn: 
  41.         tEVENTINFO  = (
  42.             {----Name}
  43.             npszName:    tOffset(@EventClickInName);
  44.             {----Number of parameters}
  45.                 cParms:    2;
  46.             {----Number of words}
  47.             cwParms:     4;
  48.             {----Pointer to parm types}
  49.             npParmTypes:        tOffset(@ParmamTypes_ClickIn);
  50.             {Pointer to Argument string}
  51.             npszParmProf:        tOffSet(@EventClickInParm);
  52.             fl:            0
  53.     );
  54.  
  55.     EventClickOutName:        array[0..9] of Char = 'ClickOut'#0;
  56.  
  57.     Event_ClickOut: tEVENTINFO  = (
  58.         npszName:         tOffset(@EventClickOutName);
  59.         cParms:        0;
  60.         cwParms:     0;
  61.         npParmTypes:    0;
  62.         npszParmProf:    0;
  63.         fl:                0
  64.     );
  65.  
  66.     Circle_Events: array[EVENT_Index] of ofsPEVENTInfo = (
  67.         ofsPEventInfo(@Event_ClickIn),
  68.         ofsPEventInfo(@Event_ClickOut),
  69.         PEVENTINFO_STD_DRAGDROP,
  70.         PEVENTINFO_STD_DRAGOVER,
  71.         0);
  72.  
  73. implementation
  74.  
  75. end.
  76.  
  77.  
  78. Listing 1
  79.  
  80. In Listing 1 we first code the Event index.  This index will be used later when defining the actual Event List (Circle_Events). Next we assign the types of parameters we will be passing between the VBX and Visual Basic.  The Argument Type Flags are defined in the VB_API_ file provided on the disk that comes with this magazine as well as in list 2.
  81.  
  82.  
  83. Argument Type Flags
  84.  
  85. Value        Description
  86. ET_I2        16-bit signed integer
  87. ET_I4        32-bit signed integer
  88. ET_R4    4-byte real
  89. ET_R8    8-byte real
  90. ET_CY    8-byte currency
  91. ET_HLSTR    String    Strings are represented in the argument structure as a handle (HLSTR) rather than a pointer.
  92.  
  93. Listing 2  
  94.  
  95. Next we assign the EVENTInfo structure.  The first element in the structure is the Event Name (ClickIn) as it will appear in the Code window in Visual Basic.  This is followed by the number of arguments being being passed and the number of computer words in the argument list.  In the ClickIn event we will be passing 2 parameters consisting of 4 words. (Two ET_R4's or 2 4-byte reals.)  Next come the pointer to the Argument Type array followed by the pointer to the Argument string.  In C (or C++) this could be the actual string since C passed the address rather than the string. Finally  we have the fl flag that can contain either a 0 or the EF_fNoUnload flag.  When this flag is set Visual Basic will not allow unloading of the current form or any control on the form when the corresponding event procedure is being fired. This flag is primarily used for events that must assume that nothing is unloaded such as the Standard Paint event.
  96.   After defining the remainder of the Events we define the Event List itself.  This list will be used in the model table to point Visual Basic to the array structures we just described.
  97. Firing an Event
  98.     Firing the ClickIn Event causes the mouse current coordinates to be passed to the Visual Basic program. 
  99.  
  100. Fire the ClickIn Event
  101.  
  102. { TYPEDEF for parameters to the ClickIn event.
  103. }
  104. Type
  105.     tagCLICKINPARMS = record
  106.         Y:   pointer;
  107.         X:    pointer;
  108.         Index:    LPVoid;
  109.     end;
  110.  
  111.  
  112. { Fire the ClickIn event, passing the x,y coords of the click.}
  113. procedure FireClickIn(Control: hctl; x, y: integer);
  114. var
  115.     params:    tagClickInParms;
  116.     xTwips,
  117.     yTwips:    LongInt;
  118. begin
  119. {    float     xTwips, yTwips;}
  120.  
  121.     xTwips := VBXPixelsToTwips(x);
  122.     yTwips := VBYPixelsToTwips(y);
  123.     params.X := @xTwips;
  124.     params.Y := @yTwips;
  125.     VBFireEvent(Control,         ord(EVENT_CIRCLE_CLICKIN),         @params);
  126. end;
  127. Listing 3
  128.  
  129. The FireClickIn function converts the Windows X and Y coordinates from pixels to twips. (that  the developer uses in the Visual Basic environment) To perform the pixel conversion the VBAPI_ routines VBXPixelsToTwips and VBYPixelsToTwips are called to ensure the coordinates are converted to logical twips that relate to the particular display device. Pointers to the converted variables are then passed to the argument structure and VBFireEvent is called.
  130.    The call to VBFireEvent does not return until the Visual Basic event procedure (assuming the developer coded one) returns.  This ensures that all of the local variables in the control code are valid locations. If the event procedure changes any of the arguments then upon return the local variable will been replaced by Visual Basic.
  131.  
  132. function CircleCtlProc(Control: HCtl; 
  133.     Wnd: Hwnd; Msg, wp: Word; 
  134.     lp: LongInt):LongInt;
  135. var
  136.     ps:        tPaintStruct;
  137.     LpCirc:    pCirc2;
  138.     hDcHold:    hDc;
  139. begin
  140.     case Msg of
  141.         WM_NCCREATE:    begin
  142.             LpCirc := VBDerefControl(Control);
  143.             LpCirc^.CircleShape := 0;
  144.             lpCirc^.FlashColor := 128;
  145.             VBSetControlProperty(Control,         ord(IPROP_Circle_BACKCOLOR),255);
  146.         end;
  147.         WM_LBUTTONDOWN,
  148.         WM_LBUTTONDBLCLK:
  149.         if (InCircle(Control, lp, HiWord(lp)))  then         begin
  150.                 hDcHold := GetDC(Wnd);
  151.                 FlashCircle(Control, hDcHold);
  152.                 ReleaseDC(Wnd, hDcHold);
  153. {---pass the mouse coord from the high and low words of lp)
  154.     FireClickIn(Control,lp,HiWord(lp));
  155.             end else FireClickOut(Control);
  156.         WM_LBUTTONUP:
  157.             if (InCircle(Control, lp, HIWORD(lp)))                 then begin
  158.                 hDcHold := GetDC(Wnd);
  159.                 PaintCircle(Control, Wnd,                     hDcHold);
  160.                 ReleaseDC(Wnd, hDcHold);
  161.             end;
  162.         WM_PAINT:
  163.             if (wP <> 0) then
  164.                 PaintCircle(Control, Wnd, wP)
  165.             else begin
  166.                 BeginPaint(Wnd, ps);
  167.                 paintCircle(Control, Wnd,                     ps.hdc);
  168.                 EndPaint(Wnd, ps);
  169.             end;
  170.         WM_SIZE:  RecalcArea(Control, Wnd);
  171.         VBM_SETPROPERTY:
  172.             case wP of
  173.                 ord(IPROP_Circle_Shape): begin
  174.                     lpCirc :=                     VBDerefControl(Control);
  175.                     lpCirc^.CircleShape := lp;
  176.                     RecalcArea(Control, Wnd);
  177.                     InvalidateRect(Wnd, nil,                         true);
  178.                     CircleCtlProc := 0;
  179.                     exit;
  180.         end;      end;  end;
  181.     CircleCtlProc := VBDefControlProc(Control,             Wnd, Msg, wP, lP);
  182. end;  
  183. end.
  184.  
  185. Listing 4
  186.  
  187. The Circle control procedure fires the ClickIn event whenever the user clicks the mouse button while in the circle. The InCircle routine (see the code included on the disk) determines if the current mouse coordinates are within the circle and then the circle color is flashed and the FireClickIn procedure calls the VBFireEvent function.
  188.    I have just described how a custom event gets processed by the Visual Basic environment.  A standard event is handled in much the same way except that the VB environment handles them automatically for you. In our event list we included the DRAGDROP and the  DRAGOVER events that are two of the standard events provided for in Visual Basic. (See list 6)
  189.  
  190. Standard Events
  191. Visual Basic provides 18 standard events. These events only have to be declared in the EVENTINFO table.  The default control procedure VBDefControlProc will normally fire the standard events in response to the various Windows messages however if you wish you may fire a standard event in the same way you fire your own custom events.
  192.  
  193. Event        Description
  194. Click    PEVENTINFO_STD_CLICK
  195.             WM_LBUTTONUP message. Fired when the control has captured the mouse. The standard MouseDown and MouseUp events occur before this event.
  196. DblClick    PEVENTINFO_STD_DBLCLICK
  197.             WM_LBUTTONDBLCLK message. Similar to Click.
  198. DragDrop    PEVENTINFO_STD_DRAGDROP
  199.             Fired after a VBM_DRAGDROP message is received.
  200. DragOver    PEVENTINFO_STD_DRAGOVER
  201.             Fired after a VBM_DRAGOVER message is received.
  202. GotFocus    PEVENTINFO_STD_GOTFOCUS
  203.             After receiving a WM_GOTFOCUS message a VBM_FIREEVENT message is posted. This, in effect, places the event in the normal queue and allows pending messages to be processed.
  204. KeyDown        PEVENTINFO_STD_KEYDOWN
  205.             Fired when a WM_KEYDOWN or a WM_SYSKEYDOWN message is received.
  206. KeyPress    PEVENTINFO_STD_KEYPRESS
  207.             Fired when a WM_CHAR message is received.
  208. KeyUp        PEVENTINFO_STD_KEYUP
  209.             Fired when a WM_KEYUP or a WM_SYSKEYUP message is received.
  210. LostFocus    PEVENTINFO_STD_LOSTFOCUS
  211.             Fired when a WM_LOSTFOCUS message is received. The same delay process as GotFocus is used here to ensure messages get processed in sequence.
  212. MouseDown    PEVENTINFO_STD_MOUSEDOWN
  213.             Fired if any BUTTONDOWN (left, right or middle) message is received. When this event is fired the mouse is captured by the control.
  214. MouseMove    PEVENTINFO_STD_MOUSEMOVE
  215.             Fired when a WM_MOUSEMOVE message is received.
  216. MouseUp        PEVENTINFO_STD_MOUSEUP
  217.             Fired if and BUTTONDOWN (left, right or middle) message is received. The mouse capture is released by this event.
  218.  
  219. Listing 5
  220.  
  221. Events unique to VB 2.0
  222.   These standard events are included for information only.  See the CDK documentation for more information about them.
  223.  
  224. Last                 PEVENTINFO_STD_LAST
  225. LinkClose         PEVENTINFO_STD_LINKCLOSE
  226. LinkError         PEVENTINFO_STD_LINKERROR
  227. LinkNotify         PEVENTINFO_STD_LINKNOTIFY
  228. LinkOpen         PEVENTINFO_STD_LINKOPEN
  229. None                PEVENTINFO_STD_NONE
  230.         None is used as a placeholder for events you wish to remove. Using this placeholder allows applications to use different versions of a control with less likelyhood of a Visual Basic abort during load.
  231. Listing 6
  232.  
  233.  
  234. Reference: Professional Features Book 1, Microsoft Visual Basic 3.0 Custom Control Guide
  235.  
  236.  
  237.  
  238. copyright ⌐ 1994 Fred C. Hill, all rights reserved.
  239.  
  240.  
  241. Fred Hill is president of Micro System Solutions. His company produces DOS and Windows applications in Borland Pascal 7.0 and Visual Basic 3.0. He may be reached on Compuserve 76060,102
  242.